package ch03.backtrack;

import java.util.ArrayList;
import java.util.List;

public class Parser {
    Lexer input;                    // 词法单元的来源
    List<Integer> markers;          // 栈，存放用于记录位置的位标（标记）
    List<Token> lookahead;          // 大小可变的缓冲区
    int p = 0;                      // 当前向前看词法单元的下标

    public Parser(Lexer input) {
        this.input = input;
        markers = new ArrayList<Integer>();         // 创建markers 栈
        lookahead = new ArrayList<Token>();         // 创建缓冲区(lookahead buffer)
        sync(1);                                  // 最少用一个token来初始化buffer
    }
    // LT(1)会返回lookahead[p]

    public Token LT(int i) {
        sync(i);
        return lookahead.get(p + i - 1);
    }

    public int LA(int i) {
        return LT(i).type;
    }

    public void match(int x) throws MismatchedTokenException {
        if (LA(1) == x) {
            consume();
        } else {
            throw new MismatchedTokenException("expecting " + input.getTokenName(x) + " found " + LT(1));
        }
    }

    /**
     * 确保当前位置p之前有i个词法单元
     * @param i
     */
    public void sync(int i) {
        if (p + i - 1 > (lookahead.size() - 1)) {               // 词法单元是否越界
            int n = (p + i - 1) - (lookahead.size() - 1);       // 获取n个词法单元
            fill(n);
        }
    }


    /**
     * 加入n个词法单元
     * @param n
     */
    public void fill(int n) {
        for (int i = 1; i <= n; i++) {
            lookahead.add(input.nextToken());
        }
    }


    public void consume() {
        p++;
        // 非推断状态，而且到达向前看缓冲区的末尾
        if (p == lookahead.size() && !isSpeculating()) {
            // 到了末尾，就该重新从0开始填入新的词法单元
            p = 0;
            lookahead.clear();              // 大小清0， 但不回收内存
        }
        sync(1);                         // 取一个新词法单元
    }

    /**
     * 标记当前位置，供release使用
     * @return
     */
    public int mark() {
        markers.add(p);
        return p;
    }

    /**
     * 将消费掉的token重新放回去
     */
    public void release() {
        int marker = markers.get(markers.size() - 1);
        markers.remove(markers.size() - 1);
        seek(marker);
    }

    public void seek(int index) {
        p = index;
    }

    public boolean isSpeculating() {
        return markers.size() > 0;
    }
}

